package com.brillilab.starter.controller.common.file;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.brillilab.common.alioss.*;
import com.brillilab.common.entity.ResponseVo;
import com.brillilab.common.utils.FileSecurityUtil;
import com.brillilab.common.utils.FileTypeUtil;
import com.brillilab.common.utils.HttpClientUtil;
import com.brillilab.domain.enums.file.UploadStausEnum;
import com.brillilab.domain.po.experiments.Experiment;
import com.brillilab.domain.po.lab.LabDevice;
import com.brillilab.domain.po.system.FileManage;
import com.brillilab.domain.po.topics.Topic;
import com.brillilab.domain.po.user.Users;
import com.brillilab.domain.vo.system.FileManageVo;
import com.brillilab.domain.vo.user.UserInfoVo;
import com.brillilab.service.core.experiments.IExperimentService;
import com.brillilab.service.core.lab.ILabDeviceService;
import com.brillilab.service.core.lab.ILabMemberService;
import com.brillilab.service.core.system.IFileManageService;
import com.brillilab.service.core.topics.ITopicDirectionService;
import com.brillilab.service.core.topics.ITopicExperimentService;
import com.brillilab.service.core.topics.ITopicService;
import com.brillilab.service.core.user.IUsersService;
import com.brillilab.service.logic.file.FileManageLogic;
import com.brillilab.service.logic.topics.TopicLogic;
import com.brillilab.service.logic.user.UsersLogic;
import com.brillilab.starter.constant.StarterConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

//import java.sql.Date;

/**
 * 　　* @description: TODO
 * 　　* @param ${tags}
 * 　　* @return ${return_type}
 * 　　* @throws
 * 　　* @author zlb
 * 　　* @date 2019/5/8 13:55
 */
@Slf4j
@RestController
@RequestMapping("/api/fileManage")
@Component
public class FileManageController {

    private static Logger logger = LoggerFactory.getLogger(FileManageController.class);

    @Resource
    private AliOssUtil aliOssUtil;

    @Resource
    private AliOssConfig aliOssConfig;

    @Resource
    private OssUtils ossUtil;

    @Resource
    private UsersLogic usersLogic;

    @Resource
    private IUsersService usersService;

    @Resource
    private IFileManageService fileManageService;

    @Resource
    private FileManageLogic fileManageLogic;

    @Resource
    private ITopicService topicService;

    @Autowired
    private TopicLogic topicLogic;

    @Resource
    private IExperimentService experimentService;

    @Resource
    private ILabDeviceService labDeviceService;

    @Resource
    private ITopicDirectionService topicDirectionService;

    @Resource
    private ITopicExperimentService topicExperimentService;
    @Resource
    private ILabMemberService labMemberService;

    /**
     * 获取OSS上传参数
     *
     * @param attachType
     * @param token
     * @return
     */
    @RequestMapping(value = "/uploadParam", method = RequestMethod.GET)
    public ResponseVo getUploadParam(Integer attachType, HttpServletRequest request, @RequestHeader(value = StarterConstant.TOKEN) String token) {

        Assert.notNull(attachType, "附件类型参数缺失！");
        Assert.notNull(OssFileEnum.getValue(attachType), "附件类型有误，请核实！");
        Map<String, Object> resultMap = new HashMap<>();

        AliSts sts = ossUtil.getThirdAppUploadPermission();
        UserInfoVo userInfo = usersLogic.getUserByToken(token);
        String basePath;
        if (OssFileEnum.PersonHeadImg.getKey().equals(attachType)) {
            //头像-公共
            basePath = ossUtil.getFileBasePath(attachType, userInfo.getId());
        } else if (OssFileEnum.Method.getKey().equals(attachType)) {
            //方法-个人
            basePath = ossUtil.getFileBasePath(attachType, userInfo.getId());
        } else {
            //其它-实验室
            Assert.notNull(userInfo.getLabId(), "用户未加入实验室！");
            basePath = ossUtil.getFileBasePath(attachType, userInfo.getLabId());
        }
        String publicHost = ossUtil.getPublicHost();

        resultMap.put("aliSts", sts);
        resultMap.put("basePath", basePath);
        resultMap.put("bucketName", ossUtil.getBucketName(attachType));
        resultMap.put("host", ossUtil.getHostUrl(attachType));
        resultMap.put("endpoint", aliOssConfig.getEndPoint());
        //回调地址
        String callbackUrl = getRequestRoot(request.getRequestURL().toString()) + "/api/fileManage/callback";
        resultMap.put("callbackUrl", callbackUrl);
        resultMap.put("callbackBody",
                "{\\\"bucket\\\":${bucket}, \\\"filePath\\\":${object},\\\"size\\\":${size}," +
                        "\\\"mimeType\\\":${mimeType},\\\"etag\\\":${etag}," +
                        "\\\"attachType\\\":" + attachType + "," +
                        "\\\"fileName\\\":${x:filename}," +
                        "\\\"audioLength\\\":${x:audiolength}," +
                        "\\\"previewImg\\\":${x:previewimg}}");
        resultMap.put("callbackBodyType", "application/json");

        //自定义参数项
        List<String> callbackVar = new ArrayList<>();
        callbackVar.add("x:filename");
        callbackVar.add("x:audiolength");
        callbackVar.add("x:previewimg");
        resultMap.put("callbackVar", callbackVar);
        return ResponseVo.success(resultMap);
    }

    /**
     * 获取OSS上传Policy和回调参数
     *
     * @param attachType 附件类型
     * @param request
     * @param token      用户token
     * @return
     */
    @RequestMapping(value = "/permission", method = RequestMethod.GET)
    public ResponseVo getUploadPolicyAndCallback(Integer attachType, HttpServletRequest request, @RequestHeader(value = StarterConstant.TOKEN) String token) {

        Assert.notNull(attachType, "请求参数【attachType】缺失！");
        Assert.notNull(OssFileEnum.getValue(attachType), "附件类型有误，请核实！");
        Map<String, Object> resultMap = new HashMap<>();
        String callbackUrl = getRequestRoot(request.getRequestURL().toString()) + "/api/fileManage/callback";

        String dir = "";//上传文件目录前缀
        String host = ossUtil.getHostUrl(attachType);

        UserInfoVo userInfo = usersLogic.getUserByToken(token);
        if (OssFileEnum.PersonHeadImg.getKey().equals(attachType)) {
            //头像-公共
            dir = ossUtil.getFileBasePath(attachType, userInfo.getId());
        } else if (OssFileEnum.Method.getKey().equals(attachType)) {
            //方法-个人
            dir = ossUtil.getFileBasePath(attachType, userInfo.getId());
        } else {
            //其它-实验室
            Assert.notNull(userInfo.getLabId(), "用户未加入实验室！");
            dir = ossUtil.getFileBasePath(attachType, userInfo.getLabId());
        }

        OSSClient ossClient = ossUtil.initClient();
        try {
            long expireTime = 60 * 2;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);

            Map<String, String> respMap = new LinkedHashMap<String, String>();
            respMap.put("accessid", aliOssConfig.getAccessKeyId());
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
            // respMap.put("expire", formatISO8601Date(expiration));


            JSONObject jasonCallback = new JSONObject();
            jasonCallback.put("callbackUrl", callbackUrl);
            jasonCallback.put("callbackBody",
                    "{\\\"bucket\\\":${bucket}, \\\"filePath\\\":${object},\\\"size\\\":${size}," +
                            "\\\"mimeType\\\":${mimeType},\\\"etag\\\":${etag}," +
                            "\\\"attachType\\\":${x:attachtype}," +
                            "\\\"fileName\\\":${x:filename}," +
                            "\\\"audioLength\\\":${x:audiolength}," +
                            "\\\"previewImg\\\":${x:previewimg}}");
            jasonCallback.put("callbackBodyType", "application/json");
            String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());
            respMap.put("callback", base64CallbackBody);

            return ResponseVo.success(respMap);
        } catch (Exception e) {
            logger.error("getUploadPolicyAndCallback-Err:" + e.getMessage());
            return null;
        }
    }

    /**
     * 服务端签名直传上传回调
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @RequestMapping(value = "/callback", method = RequestMethod.POST)
    public ResponseVo uploadCallback(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String ossCallbackBody = ossUtil.getPostBody(request.getInputStream(), Integer.parseInt(request.getHeader("content-length")));
        boolean ret = verifyOSSCallbackRequest(request, ossCallbackBody);
        Long attachId = 0l;
        if (ret) {
            //业务逻辑处理
            String callbackJson = ossCallbackBody.replace("\\", "");
            //logger.info("dataJson:" + callbackJson);
            JSONObject jsonObject = JSON.parseObject(callbackJson);
            attachId = fileManageService.uploadCallBack(jsonObject);
            Map<String, Object> resData = new LinkedHashMap<>();
            resData.put("attachId", attachId);
            //resData.put("callBackInfo",callbackJson);
            if (jsonObject != null) {
                //自定义参数
                String previewImg = jsonObject.getString("previewImg");
                Integer audioLength = jsonObject.getInteger("audioLength");
                String fileName = jsonObject.getString("fileName");
                Long fileSize = jsonObject.getLong("size");
                resData.put("previewImg", previewImg == null ? "" : previewImg);
                resData.put("audioLength", audioLength == null ? "0" : audioLength);
                resData.put("name", fileName == null ? "" : fileName);
                resData.put("size", fileSize == null ? "0" : fileSize);
            }
            return ResponseVo.success(resData, "success");
        } else {
            return ResponseVo.failure("签名验证失败!");
        }
    }

    /**
     * 提交待上传文件列表相关信息
     *
     * @param token
     * @param fileData
     * @return
     */
    @RequestMapping(value = "/submitWaitUpload", method = RequestMethod.POST)
    public ResponseVo submitWaitUpload(@RequestHeader(value = StarterConstant.TOKEN) String
                                               token, @RequestBody JSONObject fileData) {
        Assert.isTrue(fileData != null, "请求参数缺失！");

        UserInfoVo userInfo = usersLogic.getUserByToken(token);
        Long labId = userInfo != null ? userInfo.getLabId().intValue() : 0L;
        try {
            JSONArray jsonArr = fileData.getJSONArray("fileList");
            if (jsonArr == null || jsonArr.size() <= 0) {
                Assert.notNull(null, "文件为空，请添加文件！");
            }
            List<FileManageVo> fileList = JSONObject.parseArray(fileData.getJSONArray("fileList").toJSONString(), FileManageVo.class);

            List<Map<String, Object>> dataList = new ArrayList<>();
            FileManage fileManage = null;
            for (FileManageVo file : fileList) {
                Integer attachType = file.getAttachType();
                String bucketName = ossUtil.getBucketName(attachType);
                Long referDataId = file.getReferDataId();
                String fileName = file.getFileName();
                Assert.isTrue(!fileName.contains("%"),"文件名包含特殊字符，请核实！"+fileName);

                String mimeType = file.getMimeType();

                int fileType = 0;//文件类型(0文件，1音频，2视频，3图片)
                fileType = FileTypeUtil.getFileType(fileName);

                fileManage = new FileManage();
                fileManage.setBucketName(bucketName);
                fileManage.setAttachType(attachType);
                fileManage.setReferDataId(referDataId);
                fileManage.setLabId(labId);
                fileManage.setUploadStatus(UploadStausEnum.Uploading.getValue());//默认为正在上传
                fileManage.setName(fileName);
                fileManage.setMimeType(mimeType);
                fileManage.setFileType(fileType);
                FileManage fileModel = fileManageService.insert(fileManage);
                //返回插入后的文件信息
                Map<String, Object> resMap = new HashMap<>();
                resMap.put("attachId", fileModel.getId());
                resMap.put("bucketName", fileModel.getBucketName());
                resMap.put("referDataId", fileModel.getReferDataId());
                resMap.put("fileName", fileModel.getName());
                resMap.put("attachType", fileModel.getAttachType());

                dataList.add(resMap);
            }
            Map<String, Object> resDataList = new HashMap<>();
            resDataList.put("fileList", dataList);

            return ResponseVo.success(resDataList);

        } catch (Exception e) {
            log.error("提交待上传文件列表相关信息-Err：", e);
            return ResponseVo.failure("系统异常");
        }
    }

    /**
     * 绑定附件数据关联ID
     *
     * @param token
     * @param json
     * @return
     */
    @RequestMapping(value = "/bindAttachReferDataId", method = RequestMethod.POST)
    public ResponseVo bindAttachReferDataId(@RequestHeader(value = StarterConstant.TOKEN) String token,
                                            @RequestBody JSONObject json) {
        Assert.notNull(json, "请求参数缺失！");
        Long referDataId = json.getLong("referDataId");
        try {
            JSONArray jsonArr = json.getJSONArray("attachIdArr");
            if (jsonArr == null || jsonArr.size() <= 0) {
                Assert.notNull(null, "附件ID为空！");
            }
            List<Long> dataList = (List) json.getJSONArray("attachIdArr");
            for (Long attachId : dataList) {
                FileManage fileManage = fileManageService.selectById(attachId);

                if (fileManage != null && fileManage.getReferDataId() == 0) {
                    //更新数据关联ID
                    fileManage.setReferDataId(referDataId);
                    fileManage.setUpdateTime(new Date());
                    fileManageService.updateById(fileManage);
                }
            }
            return ResponseVo.success();

        } catch (Exception e) {
            log.error("绑定附件数据关联ID异常：", e);
            return ResponseVo.exception(e);
        }
    }

    /**
     * 更新文件的上传信息
     *
     * @param json
     * @param token
     * @return
     */
    @PostMapping("/updateUploadInfo")
    public ResponseVo updateUploadInfo(@RequestHeader(value = StarterConstant.TOKEN) String token,
                                       @RequestBody JSONObject json) {
        Assert.notNull(json, "请求参数缺失！");
        Long attachId = json.getLong("attachId");
        Assert.isTrue(attachId > 0, "附件ID有误！");
        String filePath = json.getString("filePath");
        Long fileSize = json.getLong("fileSize");
        Assert.notNull(fileSize, "请求参数【fileSize】缺失！");
        Integer uploadStatus = json.getInteger("uploadStatus");
        Assert.notNull(uploadStatus, "请求参数【uploadStatus】缺失！");

        Integer audioLength = json.getInteger("audioLength");
        //String previewImg = json.getString("previewImg");

        FileManage fileManage = fileManageService.selectById(attachId);
        Assert.notNull(fileManage, "附件不存在，请核实！");

        fileManage.setPath(filePath);
        fileManage.setSize(fileSize);
        fileManage.setUploadStatus(uploadStatus);
        fileManage.setAudioLength(audioLength);
        //fileManage.setPreviewImg(previewImg);
        fileManage.setUpdateTime(new Date());

        if (UploadStausEnum.Uploaded.getValue().equals(uploadStatus)) {
            //上传成功更新回调时间
            fileManage.setCallbackTime(new Date());
        }

        boolean flag = fileManageService.updateById(fileManage);
        return flag ? ResponseVo.success() : ResponseVo.failure();
    }

    /**
     * 绑定视频预览图
     *
     * @param json
     * @param token
     * @return
     */
    @PostMapping("/bindVideoPrivewImg")
    public ResponseVo bindVideoPrivewImg(Long videoAttacthId, Long imgAttachId, @RequestHeader(value = StarterConstant.TOKEN) String token) {
        Assert.notNull(videoAttacthId, "请求参数【videoAttacthId】缺失！");
        Assert.notNull(imgAttachId, "请求参数【imgAttachId】缺失！");

        FileManage videoFile = fileManageService.selectById(videoAttacthId);
        FileManage imgFile = fileManageService.selectById(imgAttachId);
        Assert.notNull(videoFile, "视频附件不存在，请核实！");
        Assert.notNull(imgFile, "预览图附件不存在，请核实！");

        Assert.isTrue(imgFile.getAttachType().equals(OssFileEnum.VideoPreviewImg.getKey()), "预览图附件类型有误，请核实！");
        videoFile.setPreviewImg(imgFile.getPath());
        videoFile.setUpdateTime(new Date());
        //执行视频预览图更新绑定
        boolean flag = fileManageService.updateById(videoFile);
        return flag ? ResponseVo.success() : ResponseVo.failure();
    }

    /**
     * 获取自动下载文件列表
     *
     * @param deviceNo
     * @return
     */
    @RequestMapping(value = "/getAutoDownFilesAIO", method = RequestMethod.GET)
    public ResponseVo getAutoDownFilesAIO(String deviceNo) {
        Assert.notNull(deviceNo, "请求参数缺失！");
        LabDevice labDevice = labDeviceService.selectByNo(deviceNo);
        Assert.isTrue(labDevice != null, "设备编号有误，请核实！");
        Long labId = labDevice.getLabId();
        //获取实验结果数据
        List<FileManage> fileList = fileManageService.getAutoFileListByLabId(labId);
        return ResponseVo.success(fileList);
    }

    /**
     * 获取自动下载文件列表
     *
     * @param token
     * @return
     */
    @RequestMapping(value = "/getAutoDownFiles", method = RequestMethod.GET)
    public ResponseVo getAutoDownFiles(@RequestHeader(value = StarterConstant.TOKEN) String token) {
        Assert.notNull(token, "token缺失！");
        UserInfoVo userInfo = usersLogic.getUserByToken(token);
        Assert.isTrue(userInfo != null, "token有误，请核实！");

        Long labId = userInfo.getLabId();
        boolean owner = labMemberService.isOwner(labId, userInfo.getLabMemberId());

        //获取实验结果数据
        List<FileManage> fileList = new ArrayList<>();
        if (owner) {
            fileList = fileManageService.getAutoFileListByLabId(labId);
        } else {
            //通过实验室Id和实验室成员Id，获取当前成员所在的所有实验Id集合
            List<Long> experimentIdList = topicLogic.getMyProjectExperimentIds(labId, userInfo.getLabMemberId());
            if (experimentIdList != null && experimentIdList.size() > 0) {
                //通过实验Id集合获取对应的实验结果数据
                fileList = fileManageService.getAutoFileListByExperimentIdList(experimentIdList);
            }
        }

        //PC端暂时过滤分析后缩略图的返回
        if(!CollectionUtils.isEmpty(fileList)){
            fileList=fileList.stream().filter(p->!p.getAttachType().equals(OssFileEnum.AnalyzeDataFileThumbnail.getKey())).collect(Collectors.toList());
        }
        return ResponseVo.success(fileList);
    }


    /**
     * 获取自动下载文件列表（APP端）
     *
     * @param token
     * @return
     */
    @RequestMapping(value = "/getAutoDownFilesAPP", method = RequestMethod.GET)
    public ResponseVo getAutoDownFilesAPP(@RequestHeader(value = StarterConstant.TOKEN) String token) {
        Assert.notNull(token, "token缺失！");
        UserInfoVo userInfo = usersLogic.getUserByToken(token);
        Assert.isTrue(userInfo != null, "token有误，请核实！");

        Long labId = userInfo.getLabId();
        boolean owner = labMemberService.isOwner(labId, userInfo.getLabMemberId());

        //获取实验结果数据
        List<FileManage> fileList = new ArrayList<>();
        if (owner) {
            fileList = fileManageService.getAutoFileListByLabId(labId);
        } else {
            //通过实验室Id和实验室成员Id，获取当前成员所在的所有实验Id集合
            List<Long> experimentIdList = topicLogic.getMyProjectExperimentIds(labId, userInfo.getLabMemberId());
            if (experimentIdList != null && experimentIdList.size() > 0) {
                //通过实验Id集合获取对应的实验结果数据
                fileList = fileManageService.getAutoFileListByExperimentIdList(experimentIdList);
            }
        }

        //APP只返回分析后的实验结果数据及分析后缩略图
        if(!CollectionUtils.isEmpty(fileList)){
            fileList=fileList.stream().filter(p->p.getAttachType().equals(OssFileEnum.AnalyzeDataFile.getKey()) || p.getAttachType().equals(OssFileEnum.AnalyzeDataFileThumbnail.getKey())).collect(Collectors.toList());
        }
        return ResponseVo.success(fileList);
    }

    /**
     * 获取已上传文件列表
     *
     * @param referDataId
     * @param token
     * @return
     */
    @RequestMapping(value = "/getUploadedList", method = RequestMethod.GET)
    public ResponseVo getUploadedList(Integer referDataId, @RequestHeader(value = StarterConstant.TOKEN) String
            token) {
        Assert.notNull(referDataId, "请求参数缺失！");
        Assert.isTrue(referDataId > 0, "referDataId参数值必须>0！");

        List<FileManage> fileList = fileManageService.getFileListByReferDataId(referDataId);
        return ResponseVo.success(fileList);
    }

    /**
     * 根据附件ID获取附件信息
     *
     * @param attachId
     * @param token
     * @return
     */
    @RequestMapping(value = "/getAttachInfo", method = RequestMethod.GET)
    public ResponseVo getAttachInfo(Long attachId, @RequestHeader(value = StarterConstant.TOKEN) String token) {
        Assert.notNull(attachId, "请求参数缺失！");
        Assert.isTrue(attachId > 0, "attachId参数值必须>0！");
        FileManage fileInfo = fileManageService.selectById(attachId);
        Assert.isTrue(fileInfo != null, "附件信息不存在，请核实！");

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("bucketName", fileInfo.getBucketName());
        resultMap.put("fileName", fileInfo.getName());
        resultMap.put("filePath", fileInfo.getPath());
        resultMap.put("fileSize", fileInfo.getSize());
        resultMap.put("previewImg", fileInfo.getPreviewImg());
        resultMap.put("audioLength", fileInfo.getAudioLength());

        return ResponseVo.success(resultMap);
    }

    /**
     * 删除文件
     *
     * @param attachId    附件ID
     * @param referDataId 业务关联ID
     * @param token       用户tokern
     * @return
     */
    @RequestMapping(value = "/deleteAttach", method = RequestMethod.GET)
    public ResponseVo deleteAttach(Long attachId, Long
            referDataId, @RequestHeader(value = StarterConstant.TOKEN) String token) {
        Assert.notNull(attachId, "请求参数缺失！");
        Assert.isTrue(attachId > 0, "attachId参数值必须>0！");
        FileManage fileInfo = fileManageService.selectById(attachId);
        Assert.isTrue(fileInfo != null, "附件信息不存在，请核实！");

        if (referDataId != null && referDataId != 0) {
            //业务关联ID非零，与原有记录验证不匹配，不允许删除
            Assert.isTrue(referDataId.equals(fileInfo.getReferDataId()), "参数【referDataId】验证失败，请核实！");
        } else {
            //业务关联ID为零，与原有记录验证不匹配，不允许删除
            Assert.isTrue(fileInfo.getReferDataId() == 0, "参数【referDataId】验证有误，请核实！");
        }
        if (!fileManageService.deleteById(attachId)) {
            return ResponseVo.failure("删除失败");
        }
        return ResponseVo.success();
    }


    /**
     * 上传文件
     *
     * @param file
     * @return
     */
    @RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
    public ResponseVo uploadFile(Integer attachType, Long referDataId,
                                 @RequestParam(name = "file", required = true) MultipartFile file, @RequestHeader(value = StarterConstant.TOKEN) String token) {
        Assert.notNull(attachType, "附件类型参数缺失！");
        Assert.notNull(OssFileEnum.getValue(attachType), "附件类型有误，请核实！");
        Assert.notNull(referDataId, "数据关联ID参数缺失！");
        try {
            Assert.notNull(file, "文件为空，请添加文件！");
            if (OssFileEnum.getIsEncrypt(attachType)) {
                Assert.isTrue(referDataId != 0, "此附件类型必须传数据关联ID！");
            }
            Assert.isTrue(file.getSize() < 100 * 1024 * 1024, "附件大小超过100M，请在PC端上传！");

            UserInfoVo userInfo;
            Long labId = 0l;
            //String bucketName = ossUtil.getBucketName(attachType);
            // 获取文件名
            String origFileName = file.getOriginalFilename();
            Assert.isTrue(!origFileName.contains("%"),"文件名包含特殊字符，请核实！");

            String suffix = origFileName.substring(origFileName.lastIndexOf(".") + 1);
            Assert.isTrue(suffix.contains("bat") == false, "文件格式有误，请核实！");
            int fileType = 0;//文件类型(0文件，1音频，2视频，3图片)
            fileType = FileTypeUtil.getFileType(origFileName);
            //String fileContentType = file.getContentType();

            if (OssFileEnum.CertificateImg.getKey().equals(attachType)) {
                //证件照上传
                labId = 0L;
                Assert.isTrue(fileType == 3, "图片类型有误");
            } else if (OssFileEnum.LabLogo.getKey().equals(attachType)) {
                //实验室Logo上传
                labId = 0L;
                Assert.isTrue(fileType == 3, "图片类型有误");
            }
            else if (OssFileEnum.AnalyzeDataFileThumbnail.getKey().equals(attachType)) {
                //分析后缩略图
                userInfo = usersLogic.getUserByToken(token);
                labId = userInfo != null ? userInfo.getLabId().intValue() : 0L;
                Assert.isTrue(fileType == 3, "图片类型有误");
            }else {
                userInfo = usersLogic.getUserByToken(token);
                labId = userInfo != null ? userInfo.getLabId().intValue() : 0L;
            }
            //文件上传初始化数据
            FileManage fileManage = fileManageLogic.initFileManage(attachType, labId, referDataId, origFileName, fileType, file.getContentType(), file.getSize());
            Assert.notNull(fileManage, "附件信息录入失败");
            //执行文件上传至OSS
            Map<String, Object> resMap;
            if (OssFileEnum.CertificateImg.getKey().equals(attachType)) {
                //证件照上传
                resMap = fileManageLogic.uploadFileNoToken(attachType, referDataId, origFileName, file.getInputStream(), fileManage);
            } else if (OssFileEnum.LabLogo.getKey().equals(attachType)) {
                //实验室Logo上传
                resMap = fileManageLogic.uploadFileNoToken(attachType, referDataId, origFileName, file.getInputStream(), fileManage);
            } else {
                userInfo = usersLogic.getUserByToken(token);
                resMap = fileManageLogic.uploadFile(attachType, origFileName, file.getInputStream(), fileManage, userInfo);
            }
            return resMap != null ? ResponseVo.success(resMap) : ResponseVo.failure("上传文件失败");
        } catch (Exception e) {
            log.error("上传文件异常：", e);
            return ResponseVo.exception(e);
        }
    }

    @RequestMapping(value = "/getFileEncryptKey", method = RequestMethod.GET)
    public ResponseVo getFileEncryptKey(Integer attachType, Long
            referDataId, @RequestHeader(value = StarterConstant.TOKEN) String token) throws IOException {
        Assert.notNull(attachType, "请求参数【attachType】缺失！");
        Assert.notNull(referDataId, "请求参数【referDataId】缺失！");

        Assert.notNull(OssFileEnum.getValue(attachType), "附件类型有误，请核实！");

        UserInfoVo userInfo = usersLogic.getUserByToken(token);
        Assert.notNull(userInfo.getLabId(), "用户未加入实验室！");

        Assert.isTrue(OssFileEnum.getIsEncrypt(attachType), "附件类型有误，请核实!");

        Long topicId = 0l;
        if (attachType >= OssFileEnum.AnalyzeDataFile.getKey() && attachType <= OssFileEnum.AnalyzeDataFileThumbnail.getKey()) {
            Experiment experiment = experimentService.selectById(referDataId);
            topicId = experiment != null ? experiment.getTopicId() : 0;

        } else if (attachType >= 500 && attachType < 600) {
            topicId = referDataId;
        }
        Topic topic = topicService.selectById(topicId);
        Assert.isTrue(topic != null, "课题不存在，请核实");
        Assert.isTrue(StringUtils.isNotBlank(topic.getFileSecretKey()), "加密key缺失，请核实");

        return ResponseVo.success(topic.getFileSecretKey());
    }

    /**
     * 文件解密
     *
     * @param attachId    附件ID
     * @param securityPwd 数据密码
     * @param request
     * @param response
     * @param token
     * @throws IOException
     */
    @RequestMapping(value = "/fileDecrypt", method = RequestMethod.GET)
    public void fileDecrypt(Long attachId, String securityPwd, HttpServletRequest request, HttpServletResponse
            response, @RequestHeader(value = StarterConstant.TOKEN) String token) throws Exception {
        Assert.notNull(attachId, "附件ID参数缺失！");
        //Assert.notNull(securityPwd, "数据密码参数缺失！");
        UserInfoVo userInfo = usersLogic.getUserByToken(token);
        Assert.notNull(userInfo.getLabId(), "用户未加入实验室！");

        Users user = usersService.selectUser(userInfo.getId(), null);
        if(StringUtils.isNotBlank(securityPwd)) {
            Assert.isTrue(user.getSecurityPwd().equals(DigestUtils.md5Hex(securityPwd).toUpperCase()), "数据密码不正确");
        }

        FileManage fileManage = fileManageService.selectById(attachId);
        Assert.notNull(fileManage, "附件不存在，请核实！");
        Assert.isTrue(userInfo.getLabId().equals(fileManage.getLabId()), "您没有权限查看他人实验室文件");
        Assert.isTrue(OssFileEnum.getIsEncrypt(fileManage.getAttachType()), "附件类型有误，请核实!");

        Assert.isTrue(fileManage.getSize() < 100 * 1024 * 1024, "附件大小超过100M，请在PC下载！");
        Long topicId = 0l;
        if (fileManage.getAttachType() >= OssFileEnum.AnalyzeDataFile.getKey() && fileManage.getAttachType() <= OssFileEnum.AnalyzeDataFileThumbnail.getKey()) {
            Experiment experiment = experimentService.selectById(fileManage.getReferDataId().longValue());
            topicId = experiment != null ? experiment.getTopicId() : 0;
        }
        if (fileManage.getAttachType() >= 500 && fileManage.getAttachType() < 600) {
            if (fileManage.getReferDataId() > 0) {
                topicId = fileManage.getReferDataId();
            }
        }
        Topic topic = topicService.selectById(topicId);

        Assert.isTrue(topic != null, "课题不存在，请核实");
        Assert.isTrue(StringUtils.isNotBlank(topic.getFileSecretKey()), "解密key有误，请核实");
        Assert.isTrue(topic.getLabId().equals(userInfo.getLabId()), "文件非法下载！");

        OSSClient ossClient = ossUtil.initClient();
        InputStream fileInputStream = OssUtils.getInputStreamByOSS(ossClient, fileManage.getBucketName(), fileManage.getPath());

        if (fileInputStream != null) {
            //解密文件流
            InputStream inputStream = FileSecurityUtil.decryptInputStream(fileInputStream, topic.getFileSecretKey());
            response.setContentType("application/octet-stream;charset=utf-8");// 指明response的返回对象是文件流
            // 写明要下载的文件的大小
            byte[] bytes = readStream(inputStream);
            response.setContentLength(bytes.length);
            //response.setContentLength(fileManage.getSize().intValue());
            String fileName = fileManage.getName();
            // 设置在下载框默认显示的文件名
            response.setHeader("Content-Disposition", "attachment; fileName=" + URLEncoder.encode(fileName, "UTF-8"));

            OutputStream outputStream = response.getOutputStream();
            outputStream.write(bytes, 0, bytes.length);
            outputStream.flush();
            //关闭流
            inputStream.close();
            outputStream.close();
        }
    }

    /**
     * 文件解密Temp
     *
     * @param attachId
     * @param securityPwd
     * @param token
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "/fileDecryptTemp", method = RequestMethod.GET)
    public void fileDecryptTemp(Long attachId, String securityPwd, String token, HttpServletRequest request, HttpServletResponse
            response) throws Exception {
        Assert.notNull(attachId, "附件ID参数缺失！");
        Assert.notNull(securityPwd, "数据密码参数缺失！");
        UserInfoVo userInfo = usersLogic.getUserByToken(token);
        Assert.notNull(userInfo.getLabId(), "用户未加入实验室！");

        Users user = usersService.selectUser(userInfo.getId(), null);
        Assert.isTrue(user.getSecurityPwd().equals(DigestUtils.md5Hex(securityPwd).toUpperCase()), "数据密码不正确");

        FileManage fileManage = fileManageService.selectById(attachId);
        Assert.notNull(fileManage, "附件不存在，请核实！");
        Assert.isTrue(userInfo.getLabId().equals(fileManage.getLabId()), "您没有权限查看他人实验室文件");
        Assert.isTrue(OssFileEnum.getIsEncrypt(fileManage.getAttachType()), "附件类型有误，请核实!");

        Assert.isTrue(fileManage.getSize() < 100 * 1024 * 1024, "附件大小超过100M，请在PC下载！");
        Long topicId = 0l;
        if (fileManage.getAttachType() >= 103 && fileManage.getAttachType() <= 107) {
            Experiment experiment = experimentService.selectById(fileManage.getReferDataId());
            topicId = experiment != null ? experiment.getTopicId() : 0;
        }
        Topic topic = topicService.selectById(topicId);

        Assert.isTrue(topic != null, "课题不存在，请核实");
        Assert.isTrue(StringUtils.isNotBlank(topic.getFileSecretKey()), "解密key有误，请核实");
        Assert.isTrue(topic.getLabId().equals(userInfo.getLabId()), "文件非法下载！");

        OSSClient ossClient = ossUtil.initClient();
        InputStream fileInputStream = OssUtils.getInputStreamByOSS(ossClient, fileManage.getBucketName(), fileManage.getPath());
        Assert.isTrue(fileInputStream != null, "文件获取失败，请核实！");
        if (fileInputStream != null) {
            //解密文件流
            InputStream inputStream = FileSecurityUtil.decryptInputStream(fileInputStream, topic.getFileSecretKey());
            //logger.info("解密完成...");
            response.setContentType("application/octet-stream;charset=utf-8");// 指明response的返回对象是文件流
            // 写明要下载的文件的大小
            byte[] bytes = readStream(inputStream);
            response.setContentLength(bytes.length);
            //response.setContentLength(fileManage.getSize().intValue());
            String fileName = fileManage.getName();
            // 设置在下载框默认显示的文件名
            response.setHeader("Content-Disposition", "attachment; fileName=" + URLEncoder.encode(fileName, "UTF-8"));

            OutputStream outputStream = response.getOutputStream();
            outputStream.write(bytes, 0, bytes.length);
            outputStream.flush();
            //关闭流
            inputStream.close();
            outputStream.close();
        }
    }

    /**
     * 文件下载
     *
     * @param attachId
     * @param request
     * @param response
     * @param token
     * @throws IOException
     */
    @RequestMapping(value = "/fileDownload", method = RequestMethod.GET)
    public void fileDownload(Long attachId, HttpServletRequest request, HttpServletResponse response,
                             @RequestHeader(value = StarterConstant.TOKEN) String token) throws Exception {
        Assert.notNull(attachId, "附件ID参数缺失！");
        UserInfoVo userInfo = usersLogic.getUserByToken(token);
        Assert.notNull(userInfo.getLabId(), "用户未加入实验室！");

        Users user = usersService.selectUser(userInfo.getId(), null);
        FileManage fileManage = fileManageService.selectById(attachId);
        Assert.notNull(fileManage, "附件不存在，请核实！");
        Assert.isTrue(userInfo.getLabId().equals(fileManage.getLabId()), "您没有权限查看他人实验室文件");
        Assert.isTrue(OssFileEnum.getIsEncrypt(fileManage.getAttachType()) == false, "附件类型有误，请核实!");
        Assert.isTrue(fileManage.getSize() < 100 * 1024 * 1024, "附件大小超过100M，请在PC下载！");

        OSSClient ossClient = ossUtil.initClient();
        InputStream fileInputStream = OssUtils.getInputStreamByOSS(ossClient, fileManage.getBucketName(), fileManage.getPath());

        Assert.isTrue(fileInputStream != null, "文件获取失败，请核实！");

        if (fileInputStream != null) {
            response.setContentType("application/octet-stream;charset=utf-8");// 指明response的返回对象是文件流
            // 写明要下载的文件的大小
            byte[] bytes = readStream(fileInputStream);
            response.setContentLength(bytes.length);
            //response.setContentLength(fileManage.getSize().intValue());
            String fileName = fileManage.getName();
            // 设置在下载框默认显示的文件名
            response.setHeader("Content-Disposition", "attachment; fileName=" + URLEncoder.encode(fileName, "UTF-8"));
            OutputStream outputStream = response.getOutputStream();
            outputStream.write(bytes, 0, bytes.length);
            outputStream.flush();
            //关闭流
            fileInputStream.close();
            outputStream.close();
        }
    }

    /**
     * 签名验证
     *
     * @param request
     * @param ossCallbackBody
     * @return
     * @throws NumberFormatException
     * @throws IOException
     */
    protected boolean verifyOSSCallbackRequest(HttpServletRequest request, String ossCallbackBody) throws
            NumberFormatException, IOException {
        boolean ret = false;
        String autorizationInput = new String(request.getHeader("Authorization"));
        String pubKeyInput = request.getHeader("x-oss-pub-key-url");
        byte[] authorization = BinaryUtil.fromBase64String(autorizationInput);
        byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput);
        String pubKeyAddr = new String(pubKey);
        if (!pubKeyAddr.startsWith("http://gosspublic.alicdn.com/") && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")) {
            logger.info("pub key addr must be oss addrss");
            return false;
        }
        String retString = HttpClientUtil.doGet(pubKeyAddr); //ossUtil.executeGet(pubKeyAddr);
        retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
        retString = retString.replace("-----END PUBLIC KEY-----", "");
        String queryString = request.getQueryString();
        String uri = request.getRequestURI();
        String decodeUri = java.net.URLDecoder.decode(uri, "UTF-8");
        String authStr = decodeUri;
        if (queryString != null && !queryString.equals("")) {
            authStr += "?" + queryString;
        }
        authStr += "\n" + ossCallbackBody;
        ret = ossUtil.doCheck(authStr, authorization, retString);
        return ret;
    }

    /**
     * 返回根请求路径的根地址
     *
     * @param url
     * @return
     */
    private String getRequestRoot(String url) {

        String rootUrl = "";
        URI effectiveURI = null;
        URI uri = URI.create(url);
        try {
            // URI(String scheme, String userInfo, String host, int port, String
            // path, String query,String fragment)
            effectiveURI = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
            rootUrl = effectiveURI.toString();
        } catch (Throwable var4) {
            effectiveURI = null;
            rootUrl = "";
        }
        return rootUrl;
    }

    /**
     * 根据文件流返回字节数组
     *
     * @param inStream
     * @return
     * @throws Exception
     */
    public byte[] readStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        return outSteam.toByteArray();
    }

    /**
     * @Description 获取文件缩略图URL
     * @params [fileId,style]
     * @return com.brillilab.common.entity.ResponseVo
     */
    @RequestMapping(value = "/getPresignedUrl",method = RequestMethod.GET)
    public ResponseVo getPresignedUrl(Long fileId,String style,
                                      @RequestHeader(value = StarterConstant.TOKEN) String token){

        Assert.isTrue(fileId!=null, "Request parameter missing!");
        usersLogic.getUserByToken(token);
        FileManage fileManage = fileManageService.selectById(fileId);
        Assert.notNull(fileManage,"File not exists !");
        URL url = aliOssUtil.getPresignedUrl(fileManage.getBucketName(),fileManage.getPath(),style);
        return ResponseVo.success(url);
    }
}
